﻿#ifndef __IO_HPP__
#define __IO_HPP__

#include "typedef.hpp"
#include <queue>
#include <fstream>

class ProgramIO
{
private:
	IOTYPE deviceType;
	IOSTATUS deviceStatus;

public:
	ProgramIO(IOTYPE type, IOSTATUS status) : deviceType(type), deviceStatus(status) {}
	~ProgramIO() = default;
	const IOTYPE getType() const{ return deviceType; }
	const IOSTATUS getStatus() const{ return deviceStatus; }
	void setStatus(IOSTATUS status) { deviceStatus = status;}
};
class SerialInput : public ProgramIO
{
private:
	uint8_t buffer[64];
	int beg = -1;
	int end = -1;

public:
	SerialInput() : ProgramIO(IOTYPE::SEIN, IOSTATUS::READY) {}
	~SerialInput() = default;
	void in();
	uint8_t out();
	void reset() { beg = end = -1; }
};
class SerialOutput : public ProgramIO
{
private:
	uint8_t buffer[64];
	int beg = -1;
	int end = -1;

public:
	SerialOutput() : ProgramIO(IOTYPE::SEOUT, IOSTATUS::READY) {}
	~SerialOutput() = default;
	void in(uint8_t);
	void out();
	void reset() { beg = end = -1; }
};
class FileInput : public ProgramIO
{
private:
	std::ifstream file;
	uint64_t faddr, maddr;
	int block;
	std::queue<std::array<uint8_t, 64>> read_buffer;

public:
	FileInput() : ProgramIO(IOTYPE::BLIN, IOSTATUS::READY) {}
	FileInput(const char *filename) : ProgramIO(IOTYPE::BLIN, IOSTATUS::READY) { file=std::ifstream(filename,std::ios::binary); }
	~FileInput() = default;
	void setStat(IOSTATUS status) { setStatus(status); }
	IOSTATUS getStat() { return getStatus(); }
	void setAddr(uint64_t addr);
	const uint64_t getAddr()const;
	void read();
};
class FileOutput : public ProgramIO
{
private:
	std::ofstream file;
	uint64_t faddr, maddr;
	int block;
	std::queue<std::array<uint8_t, 64>> write_buffer;

public:
	FileOutput() : ProgramIO(IOTYPE::BLOUT, IOSTATUS::READY) {}
	FileOutput(const char *filename) : ProgramIO(IOTYPE::BLOUT, IOSTATUS::READY) { file=std::ofstream(filename,std::ios::binary); }
	~FileOutput() = default;
	void setStat(IOSTATUS status) { setStatus(status); }
	IOSTATUS getStat() { return getStatus(); }
	void setAddr(uint64_t addr);
	const uint64_t getAddr()const;
	void write();
};
class FileIO : public ProgramIO
{
private:
	std::fstream file;
	uint64_t faddr, maddr;
	int block;
	std::queue<std::array<uint8_t, 64>> buffer;

public:
	FileIO() : ProgramIO(IOTYPE::BLIO, IOSTATUS::READY) {}
	FileIO(const char *filename) : ProgramIO(IOTYPE::BLIO, IOSTATUS::READY) { file=std::fstream(filename,std::ios::binary); }
	~FileIO() = default;
	void setStat(IOSTATUS status) { setStatus(status); }
	IOSTATUS getStat() { return getStatus(); }
	void setAddr(uint64_t addr);
	const uint64_t getAddr()const;
	void read();
	void write();
};

template <int N>
class Timer : public ProgramIO
{
private:
	uint64_t time_labels[N];
	uint64_t time;

public:
	Timer() : ProgramIO(IOTYPE::TIMER, IOSTATUS::READY) {}
	~Timer() = default;
	void start();
	void stop();
	uint64_t get_time();
};

#endif